#ifndef __M_SESSION_H__
#define __M_SESSION_H__

#include <websocketpp/server.hpp>
#include <websocketpp/config/asio_no_tls.hpp>
#include "util.hpp"
#include <unordered_map>

#define SESSION_TIMEOUT 30000
#define SESSION_FOREVER -1  

typedef enum { UNLOGIN, LOGIN } session_status;

class session {
private:
    uint64_t _ssid;
    uint64_t _uid;
    session_status _status;
    wsserver_t::timer_ptr _tp;/*定时器*/
public:
    session(uint64_t ssid) : _ssid(ssid) { DLOG("session %p 被创建!", this); }
    ~session() { DLOG("session %p 被销毁!", this); }
    void set_user(uint64_t uid) { _uid = uid; }
    void set_status(session_status status) { _status = status; }
    uint64_t get_ssid() { return _ssid; }
    uint64_t get_user() { return _uid; }
    bool is_login() { return _status == LOGIN; }
    void set_timer(const wsserver_t::timer_ptr &tp) { _tp = tp;}
    wsserver_t::timer_ptr& get_timer() { return _tp; }
};

using session_ptr = std::shared_ptr<session>;

class session_manager {
private:
    uint64_t _next_ssid;
    std::mutex _mutex;
    std::unordered_map<uint64_t, session_ptr> _sessions;
    wsserver_t *_server;
public:
    session_manager(wsserver_t *srv) : _next_ssid(1), _server(srv) { DLOG("session管理类创建成功!"); }
    ~session_manager() { DLOG("session管理类销毁成功!"); }
    session_ptr create_session(uint64_t uid, session_status status) {
        // 涉及到session id 则需要加锁
        std::unique_lock<std::mutex> lock(_mutex);
        session_ptr ssp(new session(_next_ssid));
        ssp->set_status(LOGIN);
        ssp->set_user(uid);
        _sessions.insert(std::make_pair(_next_ssid, ssp));
        _next_ssid++;
        return ssp;
    }
    session_ptr get_session_by_ssid(int64_t ssid) {
        std::unique_lock<std::mutex> lock(_mutex);
        auto it = _sessions.find(ssid);
        if(it == _sessions.end()){
            return session_ptr();
        }
        return it->second;
    }
    void append_session(const session_ptr &ssp) {
        std::unique_lock<std::mutex> lock(_mutex);
        _sessions.insert(std::make_pair(ssp->get_ssid(), ssp));
    }
    void set_session_expire_time(uint64_t ssid, int ms) {
        session_ptr ssp = get_session_by_ssid(ssid);
        if(ssp.get() == nullptr){
            return;
        }
        wsserver_t::timer_ptr tp = ssp->get_timer();
        if (tp.get() == nullptr && ms == SESSION_FOREVER) {
            // 1.session永久存在时，将session设置永久存在
        }
        else if (tp.get() == nullptr && ms != SESSION_FOREVER) {
            // 2.session永久存在时，设置指定时间之后被删除的任务
            wsserver_t::timer_ptr tp_tmp = 
                _server->set_timer(ms, std::bind(&session_manager::remove_session, this, ssid));
            ssp->set_timer(tp_tmp);
        }
        else if (tp.get() != nullptr && ms == SESSION_FOREVER) {
            // 3.session设置了定时删除任务时，将session设置永久存在
            // 由于取消定时任务会导致任务直接被执行(并不是立即执行)，导致<ssid, session_ptr>键值对被删除
            // 所以需要重新插入该键值对
            // 但是cancel()方法和insert()方法的执行顺序是未知的，所以不能直接调用insert
            // 需要加锁并设置0ms延迟的定时器，由定时器触发insert
            tp->cancel();
            ssp->set_timer(wsserver_t::timer_ptr());
            // _sessions.insert(std::make_pair(ssid, ssp));
            _server->set_timer(0, std::bind(&session_manager::append_session, this, ssp));
        }
        else if (tp.get() != nullptr && ms != SESSION_FOREVER) {
            // 4.session设置了定时删除任务时，重置定时删除任务
            // 取消旧定时任务
            tp->cancel();
            ssp->set_timer(wsserver_t::timer_ptr());
            // _sessions.insert(std::make_pair(ssid, ssp));
            _server->set_timer(0, std::bind(&session_manager::append_session, this, ssp));
            // 设置新定时任务
            wsserver_t::timer_ptr tp_tmp = 
                _server->set_timer(ms, std::bind(&session_manager::remove_session, this, ssid));
            ssp->set_timer(tp_tmp);

        }
    }
    void remove_session(uint64_t ssid) {
        std::unique_lock<std::mutex> lock(_mutex);
        _sessions.erase(ssid);
    }
};

#endif
